package com.aphrodite.ui.graphics;

import javax.microedition.lcdui.Graphics;

import com.aphrodite.image.ColorUtil;
import com.aphrodite.util.BitmapFont;

public final class Graphic3DUtil {
	/**
	 * Draw a 3D rectangle
	 * <p>
	 * <table>
	 * <tr>
	 * <td><img src="../../../resources/3drect.png"></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <code>highLightColor</code>=0xADAAAD<br/><code>shadowColor</code>=0xff0000 <br/><code>isRaised</code>=true<br/> <code>lineWidth</code>=15<br/>
	 * <td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * @param g
	 *            Graphics context
	 * @param highLightColor
	 * @param shadowColor
	 * @param lineWidth
	 *            of rectangle side
	 * @param isRaised
	 *            raised or lowered 3d rectangle
	 * @param x
	 *            coordinate of 3D rect
	 * @param y
	 *            coordiate of 3D rect
	 * @param width
	 *            of the 3D rect
	 * @param height
	 *            of the 3D rect
	 */
	public final static void draw3DRect(final Graphics g, int highLightColor, int shadowColor, final boolean isRaised,
			int lineWidth, int x, int y, final int width, final int height) {
		int x2 = x + width - 1;
		int y2 = y + height - 1;
		int nColor = g.getColor();
		int BEVEL_LOWERED = 0x2000;
		int ETCHED_IN = 0x8000;
		int BEVEL_RAISED = 0x1000;
		int nLowered = BEVEL_LOWERED | ETCHED_IN;
		int nBevel = BEVEL_LOWERED | BEVEL_RAISED;
		boolean bLowered = (isRaised ? 0x1000 : 0x2000 & nLowered) != 0;
		boolean bBevelled = (isRaised ? 0x1000 : 0x2000 & nBevel) != 0;
		int nHalfWidth = lineWidth / 2;

		while (lineWidth > 0) {
			g.setColor(bLowered ? shadowColor : highLightColor);
			g.drawLine(x, y, x, y2);
			g.drawLine(x, y, x2 - 1, y);
			g.setColor(bLowered ? highLightColor : shadowColor);
			g.drawLine(x2, y, x2, y2);
			g.drawLine(x + 1, y2, x2, y2);

			x++;
			y++;
			x2--;
			y2--;
			lineWidth--;
			if (bBevelled) {
				// soften the bevel by slightly fading the colors
				highLightColor = com.aphrodite.image.ColorUtil.adjustBrightness(highLightColor, 10);
				shadowColor = com.aphrodite.image.ColorUtil.adjustBrightness(shadowColor, 10);
			} else if (lineWidth == nHalfWidth) {
				int tmp = highLightColor;

				highLightColor = shadowColor;
				shadowColor = tmp;
			}
		}
		g.setColor(nColor);
	}

	private static void drawCube(boolean generateFace, int faceColor, Graphics g, int borderColor, int fillColor,
			int x, int y, int width, int height, int hOffset, int vOffset) {
		if (Math.abs(hOffset) > width) {
			throw new IllegalArgumentException("hOffset cannot be greater than width");
		}
		if (Math.abs(vOffset) > height) {
			throw new IllegalArgumentException("vOffset cannot be greater than height");
		}
		/* Second rectangle coordinates */
		int secRectY = y + vOffset;
		int secRectX = x + hOffset;
		/* draw the background rectangle with border */
		g.setColor(borderColor);
		g.drawRect(x, y, width, height);
		g.setColor(fillColor);
		g.fillRect((secRectX > x) ? x + 1 : x, (secRectY > y) ? y + 1 : y, width, height);
		/* draw the rectangle on top */

		g.setColor((generateFace) ? ColorUtil.adjustBrightness(fillColor, 20) : faceColor);
		g.fillRect(secRectX, secRectY, width, height);
		g.setColor(borderColor);
		g.drawRect(secRectX, secRectY, width, height);

		/*
		 * Drawing adjoining lines
		 * 
		 * determine if its left/right side adjoining. if the the second
		 * rectangle X coordinate is less than the first rectangle X, then its
		 * Right side adjoining else its left
		 */
		g.setColor(borderColor);
		if (secRectY > y) {
			if (secRectX < x) {

				g.setColor(fillColor);
				/* fill the top left triangle */
				g.fillTriangle(secRectX, secRectY, x, y, x, y + vOffset);
				/* fill the bottom right triangle */
				g.fillTriangle(secRectX + width + 1, secRectY + height, x + width, y + height, 1 + x + width
						- (Math.abs(hOffset)), secRectY + height - (Math.abs(vOffset)));
				g.setColor(borderColor);
				/* Triangle closing lines */
				g.drawLine(secRectX, secRectY, x, y);
				/* Triangle closing lines */
				g.drawLine(secRectX + width, secRectY + height, x + width, y + height);
				// drawRight side line=/ (TOP RIGHT)
				g.drawLine(secRectX + width, secRectY, x + width, y);
			} else {
				g.setColor(fillColor);
				/* fill top right triangle */
				g.fillTriangle(secRectX + width, secRectY, x + width, y, x + width, y + Math.abs(vOffset));
				/* fill bottom left triangle */
				g.fillTriangle(secRectX, secRectY + height, x, y + height, x + Math.abs(hOffset), y + height);
				g.setColor(borderColor);
				/* Triangle closing lines */
				g.drawLine(secRectX + width, secRectY, x + width, y);
				/* Triangle closing lines */
				g.drawLine(secRectX, secRectY + height, x, y + height);
				// draw left side line=\ (TOP LEFT)
				g.drawLine(secRectX, secRectY, x, y);
			}
		} else {/* secRect < Y */
			if (secRectX < x) {
				g.setColor(fillColor);
				/* fill top right triangle */
				g.fillTriangle(secRectX + width + 1, secRectY, x + width, y, x + (width - Math.abs(hOffset)), y);
				/* fill bottom right triangle */
				g.fillTriangle(secRectX, secRectY + height, x, y + height, x, secRectY + height + 1);
				g.setColor(borderColor);
				/* Triangle closing lines */
				g.drawLine(secRectX + width, secRectY, x + width, y);
				/* Triangle closing lines */
				g.drawLine(secRectX, secRectY + height, x, y + height);
				// drawRight side line=/ (BOTTOM RIGHT)
				g.drawLine(secRectX + width, secRectY + height, x + width, y + height);
			} else {
				g.setColor(fillColor);
				/* fill top left triangle */
				g.fillTriangle(secRectX, secRectY, x, y, x + hOffset, y);
				/* fill bottom right triangle */
				g.fillTriangle(secRectX + width, secRectY + height, x + width, y + height, x + width, secRectY + height
						+ 1);
				g.setColor(borderColor);
				/* Triangle closing lines */
				g.drawLine(secRectX, secRectY, x, y);
				/* Triangle closing lines */
				g.drawLine(secRectX + width, secRectY + height, x + width, y + height);
				// draw left side line=\ (BOTTOM LEFT)
				g.drawLine(secRectX, secRectY + height, x, y + height);
			}
		}

	}

	/**
	 * Draw a Cubical primitive
	 * <p>
	 * <b>Drawing Cubial primitive</b> <br/> <code>width</code> :20<br/>
	 * <code>height</code>: 80<br/>
	 * <table>
	 * <tr>
	 * <td><img src="../../../resources/cube_05_05.png"/><br/>
	 * <code>hOffset</code> : -5<br/> <code>vOffset</code>: 5</td>
	 * <td><img src="../../../resources/cube_05.png"/><br/> <code>hOffset</code>
	 * : 5<br/> <code>vOffset</code>: 5</td>
	 * <td><img src="../../../resources/cube_-05.png"/><br/>
	 * <code>hOffset</code> : -5<br/> <code>vOffset</code>: -5</td>
	 * <td><img src="../../../resources/cube_up.png"/><br/> <code>hOffset</code>
	 * : 5<br/> <code>vOffset</code>: -5</td>
	 * </tr>
	 * </table>
	 * </p>
	 * 
	 * 
	 * @param g
	 *            Graphics context
	 * @param borderColor
	 *            Color used by the line borders
	 * @param fillColor
	 *            The color used for filing the cube. The face of the rectangle
	 *            is the same color with added brightness
	 * @param x
	 *            coordinate of the top left corner of the cube
	 * @param y
	 *            coordinate of the top left corner of the cube
	 * @param width
	 *            of the cube
	 * @param height
	 *            of the cube
	 * @param hOffset
	 *            Horizontal offset. Can be negative or positive. but cannot be
	 *            greater than <code>width</code>
	 * @param vOffset
	 *            Vertical offset. Can be negative or positive. but cannot be
	 *            greater than <code>width</code>
	 * @throws IllegalArgumentException
	 *             If <code>hOffset</code> is greater than width
	 * @throws IllegalArgumentException
	 *             If <code>vOffset</code> is greater than height
	 */
	public static void drawCube(Graphics g, int borderColor, int fillColor, int x, int y, int width, int height,
			int hOffset, int vOffset) {
		drawCube(true, 0, g, borderColor, fillColor, x, y, width, height, hOffset, vOffset);
	}

	/**
	 * 
	 * @param g
	 * @param borderColor
	 *            Color used by the line borders
	 * @param fillColor
	 *            Color used for filing the cube. The face of the rectangle is
	 *            the same color with added brightness
	 * @param faceColor
	 *            Color of the side facing the screen
	 * @param x
	 *            coordinate of the top left corner of the cube
	 * @param y
	 *            coordinate of the top left corner of the cube
	 * @param width
	 *            of the cube
	 * @param height
	 *            of the cube
	 * @param hOffset
	 *            Horizontal offset. Can be negative or positive. but cannot be
	 *            greater than <code>width</code>
	 * @param vOffset
	 *            Vertical offset. Can be negative or positive. but cannot be
	 *            greater than <code>width</code> @throws
	 *            IllegalArgumentException If <code>hOffset</code> is greater
	 *            than width
	 * @throws IllegalArgumentException
	 *             If <code>vOffset</code> is greater than height
	 */
	public static void drawCube(Graphics g, int borderColor, int fillColor, int faceColor, int x, int y, int width,
			int height, int hOffset, int vOffset) {
		drawCube(false, faceColor, g, borderColor, fillColor, x, y, width, height, hOffset, vOffset);
	}

	/**
	 * TBD
	 * 
	 * @param g
	 * @param borderColor
	 * @param fillColor
	 * @param x
	 * @param y
	 * @param height
	 * @param width
	 */
	public static void drawPyramid(Graphics g, int borderColor, int fillColor, int x, int y, int height, int width) {

	}

	/**
	 * Create 3D text with bitmap fonts
	 * 
	 * @param g
	 * @param font
	 *            Bitmap Font
	 * @param text
	 * @param direction
	 * @param shadowColor
	 * @param textColor
	 * @param shadowWidth
	 * @param x
	 * @param y
	 * @see BitmapFont
	 */
	public static void drawString(final Graphics g, BitmapFont font, final String text, final int direction,
			final int shadowColor, final int textColor, final int shadowWidth, int x, int y) {
		g.setColor(shadowColor);
		int[] grad = ColorUtil.getGradient(shadowColor, textColor, shadowWidth);
		for (int i = 0; i < shadowWidth; i++) {
			if ((direction & GraphicUtil.DIR_LEFT) == GraphicUtil.DIR_LEFT) {
				x--;
			} else if ((direction & GraphicUtil.DIR_RIGHT) == GraphicUtil.DIR_RIGHT) {
				x++;
			}
			if ((direction & GraphicUtil.DIR_UP) == GraphicUtil.DIR_UP) {
				y--;
			} else if ((direction & GraphicUtil.DIR_DOWN) == GraphicUtil.DIR_DOWN) {
				y++;
			}
			g.setColor(grad[i]);
			g.drawString(text, x, y, 0);
		}
	}

	/**
	 * Draw 3D text with the current font.
	 * <p>
	 * <b>Creating 3D text</b><br/> <code>
	 * <table>
	 * <tr>
	 * <td>
	 * <code>shadowColor</code> : 0<br/> </td>
	 * <td>
	 * </td>
	 * <code>textColor</code>: 0xffffff<br/> </tr>
	 * <tr>
	 * <td><img src="../../../resources/3dup.png"/><br/> <code>direction</code>
	 * <br/> GraphicUtil.DIR_LEFT|GraphicUtil.DIR_UP<br/></td>
	 * <td><img src="../../../resources/3ddwn.png"/><br/> <code>direction</code>
	 * <br/>GraphicUtil.DIR_LEFT|GraphicUtil.DIR_DOWN <br/></td>
	 * </tr>
	 * <tr>
	 * <td>
	 * <img src="../../../resources/3drtup.png"/><br/> <code>direction</code>
	 * <br/>GraphicUtil.DIR_LEFT|GraphicUtil.DIR_UP <br/></td>
	 * <td>
	 * <img src="../../../resources/3drtdwn.png"/><br/> <code>direction</code>
	 * <br/>GraphicUtil.DIR_LEFT|GraphicUtil.DIR_DOWN <br/></td>
	 * <tr/>
	 * </table>
	 * </p>
	 * 
	 * @param g
	 * @param text
	 * @param direction
	 *            of the 3D text see {@link GraphicUtil#DIR_LEFT},
	 *            {@link GraphicUtil#DIR_RIGHT} , {@link GraphicUtil#DIR_UP},
	 *            {@link GraphicUtil#DIR_DOWN}
	 * @param shadowColor
	 * @param textColor
	 * @param shadowWidth
	 *            width of the shadow
	 * @param x
	 *            coordinate from where shadow starts
	 * @param y
	 *            coordinate from where shadow starts
	 */
	public static void drawString(final Graphics g, final String text, final int direction, final int shadowColor,
			final int textColor, final int shadowWidth, int x, int y) {

		g.setColor(shadowColor);
		int[] grad = ColorUtil.getGradient(shadowColor, textColor, shadowWidth);
		for (int i = 0; i < shadowWidth; i++) {
			if ((direction & GraphicUtil.DIR_LEFT) == GraphicUtil.DIR_LEFT) {
				x--;
			} else if ((direction & GraphicUtil.DIR_RIGHT) == GraphicUtil.DIR_RIGHT) {
				x++;
			}
			if ((direction & GraphicUtil.DIR_UP) == GraphicUtil.DIR_UP) {
				y--;
			} else if ((direction & GraphicUtil.DIR_DOWN) == GraphicUtil.DIR_DOWN) {
				y++;
			}
			g.setColor(grad[i]);
			g.drawString(text, x, y, 0);
		}
	}

	private Graphic3DUtil() {}
}
